﻿// <copyright file="T4ScriptFileGenerator.cs" company="T4 Toolbox Team">
//  Copyright © T4 Toolbox Team. All Rights Reserved.
// </copyright>

namespace T4Toolbox.VisualStudio
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using EnvDTE;
    using Microsoft.VisualStudio.TextTemplating;
    using Microsoft.VisualStudio.TextTemplating.VSHost;

    /// <summary>
    /// Two-stage template-based file generator.
    /// </summary>
    /// <remarks>
    /// <para>
    /// When associated with any file, this generator will produce an empty text template 
    /// with the same name as the file and .tt extension. This template will be then 
    /// transformed by the standard TextTemplatingFileGenerator. If the template already 
    /// exist, this generator will preserve its content and still trigger the second 
    /// code generation stage. 
    /// </para>
    /// <para>
    /// This code generator is used to trigger immediate T4-based code generation when a 
    /// model file is saved. For example, without this generator the user had to remember
    /// to manually transform the LINQ to SQL T4 templates every time they change the .dbml 
    /// file. However, when this file generator is associated with the .dbml file, the T4 
    /// templates are transformed automatically whenever the .dbml file is saved. 
    /// </para>
    /// </remarks>
    [Guid("1C371848-6C96-472D-B8EF-92089186CCDF")]
    public class T4ScriptFileGenerator : BaseCodeGeneratorWithSite
    {
        /// <summary>
        /// Returns default T4 script file extension.
        /// </summary>
        /// <returns>A <see cref="string"/> that contains ".tt".</returns>
        public override string GetDefaultExtension()
        {
            return ".tt";
        }

        /// <summary>
        /// Generates new or uses existing T4 script.
        /// </summary>
        /// <param name="inputFileName">
        /// A <see cref="string"/> that contains name of the file with which 
        /// <see cref="T4ScriptFileGenerator"/> is associated as a Custom Tool.
        /// </param>
        /// <param name="inputFileContent">
        /// A <see cref="string"/> that contains content of the input file.
        /// </param>
        /// <returns>
        /// Contents of the existing or newly generated T4 script file.
        /// </returns>
        protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
        {
            string templateFileName = Path.ChangeExtension(inputFileName, ".tt");

            // Check to see if the T4 Script file is open in Visual Studio editor
            Document templateDocument = this.Dte.Documents.Cast<Document>().SingleOrDefault(d => d.FullName == templateFileName);
            if (templateDocument != null && !templateDocument.Saved)
            {
                // Save the script file if it was modified
                templateDocument.Save(string.Empty);
            }
            
            string templateFileContent;
            Encoding templateEncoding;

            // If the T4 script file already exists
            if (File.Exists(templateFileName))
            {
                // Read it from disk
                templateEncoding = EncodingHelper.GetEncoding(templateFileName);
                templateFileContent = File.ReadAllText(templateFileName, templateEncoding);
            }
            else
            {
                // Otherwise, create a new, empty one
                templateEncoding = Encoding.UTF8;

                // Select the programming language for the script based on the language of the project it belongs to
                const string VisualBasicProject = "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}";
                Project currentProject = this.Dte.Solution.FindProjectItem(inputFileName).ContainingProject;
                string language = string.Compare(currentProject.Kind, VisualBasicProject, StringComparison.OrdinalIgnoreCase) == 0
                    ? "VB"
                    : "C#";

                templateFileContent = 
                    "<#@ template language=\"" + language + "\" hostspecific=\"True\" debug=\"True\" #>" + Environment.NewLine +
                    "<#@ output extension=\"txt\" #>" + Environment.NewLine +
                    "<#@ include file=\"T4Toolbox.tt\" #>" + Environment.NewLine;
            }

            return templateEncoding.GetBytes(templateFileContent);
        }
    }
}
